package com.sky.filter;

/**
 * @projectName: netty-cloud
 * @package: com.sky.filter
 * @className: CaptchaAuthenticationProvider
 * @author: zhl
 * @description: TODO
 * @date: 2024/4/29 11:09
 * @version: 1.0
 */

import com.sky.constants.SecurityConstants;
import com.sky.exception.InvalidCaptchaException;
import com.sky.service.RedisCache;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Objects;

/**
 * 验证码校验
 * 注入ioc中替换原先的DaoAuthenticationProvider
 * 在authenticate方法中添加校验验证码的逻辑
 * 最后调用父类的authenticate方法并返回
 *
 * @author vains
 */
@Slf4j
public class CaptchaAuthenticationProvider extends DaoAuthenticationProvider {

    /**
     * 利用构造方法在通过{@link Component}注解初始化时
     * 注入UserDetailsService和passwordEncoder，然后
     * 设置调用父类关于这两个属性的set方法设置进去
     *
     * @param userDetailsService 用户服务，给框架提供用户信息
     * @param passwordEncoder    密码解析器，用于加密和校验密码
     */
    public CaptchaAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        super.setPasswordEncoder(passwordEncoder);
        super.setUserDetailsService(userDetailsService);
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        log.info("Authenticate captcha...");
        // 获取当前request
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            throw new InvalidCaptchaException("Failed to get the current request.");
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

        // 获取当前登录方式
        String loginType = request.getParameter("loginType");
        if (!StringUtils.hasText(loginType)){
            // 默认使用图片验证码
            loginType = SecurityConstants.IMAGE_LOGIN_TYPE;
        }

        // 短信验证码
        if (Objects.equals(loginType, SecurityConstants.SMS_LOGIN_TYPE)) {
            log.info("It isn't necessary captcha authenticate.");
            return super.authenticate(authentication);
        }

        // 获取提交参数中的验证码
        String code = request.getParameter("code");
        if (ObjectUtils.isEmpty(code)) {
            throw new InvalidCaptchaException("The captcha cannot be empty.");
        }

        // redis中存储的验证码
        Object sessionCaptcha = "";

        // 图形验证码
        if (Objects.equals(loginType, SecurityConstants.IMAGE_LOGIN_TYPE)) {
            String uuID = request.getParameter("uuID");
            // 获取session中存储的验证码
            String key = RedisCache.captcha_codes + uuID;
            sessionCaptcha = RedisCache.get(key);
            if (sessionCaptcha == null){
                throw new InvalidCaptchaException("The captcha is incorrect.");
            }
            RedisCache.delete(key);
        }

        if (sessionCaptcha instanceof String sessionCode) {
            if (!sessionCode.equalsIgnoreCase(code)) {
                throw new InvalidCaptchaException("The captcha is incorrect.");
            }
        } else {
            throw new InvalidCaptchaException("The captcha is abnormal. Obtain it again.");
        }
        log.info("Captcha authenticated.");
        return super.authenticate(authentication);
    }
}
